/***
 * 默认的datasource选项，对常用的选项的一个封装，
 * 以便每次建立datasource都填写这些选项
 *
 * @type {{serverFiltering: boolean, serverPaging: boolean, serverGrouping: boolean, serverSorting: boolean, serverAggregates: boolean, batch: boolean, schema: {type: string, errors: DATASORUCE_DEFAULTS.schema.errors, data: string, total: string}, error: DATASORUCE_DEFAULTS.error}}
 */
var DATASORUCE_DEFAULTS = {
    serverFiltering: true,
    serverPaging: true,
    serverGrouping: true,
    serverSorting: true,
    serverAggregates: true,
    batch: true,
    schema: {
        type: "json",
        //服务端已经成功连通，但出现处理异常，如果此值不空的话，则调用下面的error函数
        errors: function (response) {
            if (response.status == "error" || response.status == "fail") {
                return response.message;
            }
        },
        data: "data.list",
        total: "data.total"
    },
    error: function (e) {
        //alert("error=" + kendo.stringify(e));
        //如果e.xhr不空，则表示是网络（服务器）异常，否则是业务异常（已经有系统处理）
        if (e.xhr) {
            //alert(e.xhr.status + ":" + e.xhr.message);
        }
        else {
            alert("status=" + e.status);
            alert("errors=" + e.errors);  // displays "Invalid query"
            this.cancelChanges();
        }
    }
};
/**
 *
 * data里含有list
 * {
      data: { list: [] }
    }
 * @param option
 * @returns {*}
 */
function dataSourceOption(option) {
    return _.merge({}, DATASORUCE_DEFAULTS, option)
}
/**
 *
 *  data对应的是一个数组
 *  {
 *
     *      data: []
     *  }
 * @returns {*}
 */
function dataSourceSimpleDataOption() {
    return dataSourceOption({serverPaging: false, schema: {data: "data"}})
}
/**
 * 创建一个远程读取数据的datasource，
 * 此函数仅仅是对kendo的read配置进行了包装并进行了部分增强，因为read是最常用的一个选项
 * 预期远程来的数据具有的格式：
 * {
 *      data: {
 *          list:[],
 *          total: number
 *      }
 * }
 * @see DATASORUCE_DEFAULTS的schema
 * data: "data.list",
 * total: "data.total"
 *
 * @param ajaxOpt
 *      ajax的选项，同于$.axja的选项
 *
 * @param dsOpt
 *     1） 配置datasource的选项，同于kendo的datasource的选项
 *     2）新增几个配置
 *      2.1)_intercept,如果存在函数且返回值为false，不进行读取
 *      2.2)_simple，如果存在且为true，则发送给服务器的数据将不被包装成{data: {要发送的数据}}，就仅仅是{要发送的数据}
 *      2.3)_translate,如果存在此函数，就对返回来的数据进行进一步的处理；
 *
 * @returns {*}
 */
function readDataSource(ajaxOpt, dsOpt) {
    var transport = {
        page: 1, pageSize: 20,
        transport: {
            read: function (options) {
                if (dsOpt && dsOpt._intercept) {
                    if (!dsOpt._intercept(options)) {
                        return;
                    }
                }
                var data = ajaxOpt.data || {}
                if (_.isFunction(data)) {
                    data = data()
                }
                if (!_.isEmpty(data)) {
                    if (dsOpt && dsOpt._simple) {

                    }
                    else {
                        data = {data: data}
                    }
                }
                var type = _.toLower(ajaxOpt.type ? ajaxOpt.type : 'post');
                var playoad = _.merge({}, options.data, data);
                if ('post' == type) {
                    playoad = kendo.stringify(playoad);
                }
                var param = _.extend({}, _.omit(ajaxOpt, 'data'), {data: playoad})
                front.ajax.fetch(param)
                    .done(function (result) {
                        if (result.status == 'ok') {
                            result = dsOpt && dsOpt._translate ? dsOpt._translate(result) : result;
                            options.success(result);
                        }
                        else {
                            options.error(result);
                        }
                    })
            }
        }
    }
    var dsOpts = _.merge({}, transport, dataSourceOption(dsOpt || {}));
    return new kendo.data.DataSource(dsOpts);
}

function progress_post(options, elm) {
    elm = elm ? elm : $('body')
    kendo.ui.progress(elm, true);
    return front.ajax.fetch(options)
        .always(function () {
            kendo.ui.progress(elm, false);
        })
}
function progress_get(options, elm) {
    elm = elm ? elm : $('body')
    kendo.ui.progress(elm, true);
    return front.ajax.fetch(_.extend({}, options, {type: 'get'}))
        .always(function () {
            kendo.ui.progress(elm, false);
        });
}

/***
 *
 * 表示一个布局组件，布局组件上能放置视图；
 * 布局组件本身也是要给html片段，此html片段，表示整个的页面布局格式，
 * 可以理解为放置其它html片段（视图）的容器。
 *
 * @param opts
 *  opts.el: string ,同于kendo.Layout的参数，是一个模版id
 *
 *  opts.views: []，视图配置列表
 *          object {
 *                  id: string, 唯一标识，在showInById的参数使用
 *                  title:string，说明性的文字，用来帮助程序员记忆视图的功用
 *                  hash: string, 展示在页面上的那个id，id一般是布局页面上一个占位div
 *                  template : string or function |url string,视图模版，
 *                             可以是一个静态字符串，此字符串可以是一个id指向一个html dom对象，也可以
 *                             是一个html片段代码，也可以是一个返回静态字符串的函数，
 *                             另也可以是一个url，系统将通过url从远端获得html片段
 *                  model：和视图对应的模型，此模型需要事先定义好
 *                  init : function ，初始化函数
 *          }
 *
 *
 *  opts.onBeforeShowIn: function，显示视图前执行的方法，对所以视图都有效
 *
 *
 * @returns {front.layout.Layout}
 *          返回一个对象
 *
 * @constructor
 * `
 */
front.layout.Layout = function (opts) {
    var el = opts.el;
    this.onBeforeShowIn = opts.onBeforeShowIn;
    this.layout = new kendo.Layout(el);
    var views = opts.views || {};
    _.forEach(views, function (vw) {
        views[vw.id] = vw;
    });
    this.views = views;
    /**
     * 获得kendo的布局对象
     *
     * @returns {kendo.Layout|*}
     */
    this.getLayout = function () {
        return this.layout;
    }
    /**
     * 依据id，获得视图配置对象
     *
     * @param id
     */
    this.findModel = function (id) {
        return this.views[id].model;
    }
    /***
     * 显示视图，调用此方法会在页面上显示指定id的视图
     *
     * @param id
     *      视图id，此id是视图配置上指定的id
     * @param shown
     *      显示视图后，所调用的函数
     */
    this.showInById = function (id, shown) {
        var view = this.views[id];
        if (!view) {
            alert('no ' + id + ' view');
            return;
        }
        var hash = view.hash;
        var model = view.model || {};
        var init = view.init;
        var onBeforeShowIn = this.onBeforeShowIn;
        if (!view.view) {
            if (view.template) {
                var tpl = view.template;
                var tplHash = _.isFunction(tpl) ? tpl() : tpl;
                view.view = new kendo.View(tplHash, {model: model, init: init});
                if (onBeforeShowIn) onBeforeShowIn();
                this.layout.showIn(hash, view.view);
                shown && shown();
            }
            else if (view.url) {
                var body = $("body");
                var lay = this.layout;
                kendo.ui.progress(body, true);
                front.ajax.get(view.url, view.data || {})
                    .done(function (html) {
                        view.view = new kendo.View(html, {model: model, init: init});
                        if (onBeforeShowIn) onBeforeShowIn();
                        lay.showIn(hash, view.view);
                        shown && shown();
                    })
                    .always(function () {
                        kendo.ui.progress(body, false);
                    })
            }
            else {
                alert(id + 'view requires a url or template');
            }
        }
        else {
            if (onBeforeShowIn) onBeforeShowIn();
            this.layout.showIn(hash, view.view);
            shown && shown();
        }
    }
    return this;
};

/***
 *
 * 处理json对象，kendo模型对象，datasource对象的类
 *
 */
front.data = (function () {
    var ObservableObject = kendo.data.ObservableObject,
        ObservableArray = kendo.data.ObservableArray,
        DataSource = kendo.data.DataSource;

    var shouldOmit = function (value, field) {
        return (value instanceof DataSource) || _.endsWith(field, '_ds') || _.startsWith(field, '_');
    }

    var self = {};

    /***
     * 重置对象：如果对象属性是数组则变为空数组，如果是其它的变为空字符串，
     * 一般用于当新建一个对象时，需要将页面上的输入清空
     *
     * 不处理：
     * 1）_开头的私有字段；
     * 2)不处理datasource对象
     * 3）不处理以_ds结尾的字段
     *
     * @param obj
     *  待重置的对象
     */
    self.reset = function (obj) {
        for (var field in obj) {
            if (obj.shouldSerialize(field)) {
                var value = obj[field];
                var isOmit = front.util.isEmpty(value) || value instanceof DataSource || _.endsWith(field, '_ds') || _.startsWith(field, '_');
                if (isOmit) {
                    continue;
                }
                if (value instanceof ObservableObject) {
                    self.reset(value)
                }
                else if (value instanceof ObservableArray) {
                    obj.set(field, []);
                }
                else {
                    obj.set(field, '');
                }
            }
        }
    }
    /***
     * 将一个对象的属性值，赋给另一个对象，
     * 一般用在，当从后端取到一个对象后，将值赋给模型对象，一般数据展示到页面上
     *
     * @param source
     *      源对象
     * @param target
     *      目标对象：此对象的字段值将被源对象替换
     * @param datePatten
     *      如果提供，将对日期类型的字段进行格式化
     */
    self.set = function (source, target, datePatten) {
        for (var field in source) {
            if (source.shouldSerialize && !source.shouldSerialize(field)) {
                continue;
            }
            var value = source[field];
            var isOmit = shouldOmit(value, field);
            if (isOmit) {
                continue;
            }
            if (datePatten && _.isDate(value)) {
                var dateVal = kendo.toString(value, datePatten);
                target.set(field, dateVal);
            }
            else {
                target.set(field, value);
            }
        }
    }
    /***
     * 使用此方法将对象转换为json对象，
     * 一般用在向服务端传输数据的时候，这个函数会剔除掉一些不需要传递的字段数据
     *
     * @param obj
     * @returns {*}
     */
    self.toJSON = function (obj) {
        var omitFields = [];
        for (var field in obj) {
            if (obj.shouldSerialize(field)) {
                var value = obj[field];
                var isOmit = shouldOmit(value, field);
                if (isOmit) {
                    omitFields.push(field);
                }
            }
        }
        var json = _.omit(obj.toJSON(), omitFields);
        return json;
    }

    /***
     * 创建一个数据源，期望远程传递过来的数据格式含： { data: [] }，
     * 即此数据源，将从data中读取数组数据
     *
     * @param ajaxOpt
     * @param dsOpt
     * @param opts
     * @returns {*}
     */
    self.dataSource = function (ajaxOpt, dsOpt, opts) {
        dsOpt = _.merge({}, dsOpt || {}, {
                schema: {
                    data: 'data'
                }
            }
        )
        return readDataSource(ajaxOpt, dsOpt, opts);

    }
    /***
     * 创建一个数据源，将获得的远程数据（数组）包含元素对象加上一个num（序号）
     *
     *
     * @param ajaxOpt
     * @param dsOpt
     * @param opts
     * @returns {*}
     */
    self.pgSeqDataSource = function (ajaxOpt, dsOpt, opts) {
        dsOpt = _.merge({}, dsOpt || {}, {
                _translate: function (result) {
                    var list = result.data.list || result.data || [];
                    var startRow = result.data.startRow || 1;
                    _.forEach(list, function (item) {
                        item.num = startRow++;
                    })
                    return result;
                }
            }
        )
        return readDataSource(ajaxOpt, dsOpt, opts);

    }
    /***
     * 包装了一下全局readDataSource方法
     *
     * @param ajaxOpt
     * @param dsOpt
     * @param opts
     * @returns {*}
     */
    self.readDataSource = function (ajaxOpt, dsOpt, opts) {
        return readDataSource(ajaxOpt, dsOpt, opts);
    }
    //返回的公有对象
    return self;
}());
